#pragma once

//要注意浏览器默认的是不是http协议

#include "log.hpp"
#include "Socket.hpp"

#include <iostream>
#include <string>
#include <pthread.h>
#include <fstream>
#include <sstream>
#include <vector>
#include <sys/stat.h>
#include <unordered_map>

const std::string wwwroot = "wwwroot";   //web根目录
const std::string homepath = "index.html"; //首页目录
const std::string sep = "\r\n";  //分隔符

const uint16_t defaultport = 8080;


//线程的结构体传参
class ThreadData
{
public:
    ThreadData(int sockfd)
    :_sockfd(sockfd)
    {}
public:
    int _sockfd;
};

//HTTP请求报文的解析类
class HttpRequest
{
public:
    //反序列化
    //Method URL HTTPVersion\r\n  Key:Value\r\nKey:Value\r\n  \r\n  content
    //请求行                        请求属性                    空行  请求正文
    void Deserialize(std::string req_package)
    {
        while(true)
        {
            //把每一行属性截取后放到req_header中
            size_t pos = req_package.find(sep);
            if(pos == std::string::npos) break;
            std::string temp = req_package.substr(0, pos);
            //如果该行是空行，则是报头和请求正文的分割，不用放进req_header中，同时break
            if(temp.empty()) break;


            req_header.push_back(temp);
            //把放入的行从报头中给删除掉
            req_package.erase(0, pos + sep.size());
        }
        //把空行删除
        req_package.erase(0, sep.size());
        //剩下的就都是请求正文了
        text = req_package;
    }
    //用于查看我们反序列化后的内容
    void DebugPrint()
    {
        std::cout << "#################################################################################" << std::endl;
        std::cout << "-----------------------被序列化后的请求报文-----------------------" << std::endl;
        for(auto& line : req_header)
        {
            std::cout << line << std::endl;
        }
        std::cout << "空行" <<std::endl;
        std::cout << text << std::endl;
        std::cout << "----------------------------------------------------------------" <<std::endl; 
        std::cout << "Method:" << method << " Url:" << url << " HTTPVerison:" << http_version << std::endl;
        std::cout << "请求的页面路径:" << req_path << std::endl;
        std::cout << "请求的资源文件后缀:" << suffix << std::endl;
        std::cout << "----------------------------------------------------------------" <<std::endl; 
        std::cout << "#################################################################################" << std::endl;
        std::cout << "\n\n";
    }

    //解析请求行的第一行、并获取用户请求的url地址
    void Parse()
    {
        //会以空格作为分割符进行输入
        std::stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;

        req_path = wwwroot;  // ./wwwroot
        //请求首页的情况
        //因为如果是请求首页时用的是"/"，如果我们不做规定，那么就会路径错误，返回我们定义的404错误了。
        if(url == "/" || url == "/index.html")  
        {
            req_path += "/";
            req_path += homepath;   // ./wwwroot + /index.html
        }
        //请求其他页面的情况
        else
            req_path += url; // ./wwwroot + /xx/xx/xx

        //获取请求资源的后缀，确定文件类型
        size_t pos = req_path.rfind(".");
        if(pos == std::string::npos)  
            suffix = ".html";   //如果没有后缀就默认为.html
        else
            suffix = req_path.substr(pos);
    }

public:
    std::vector<std::string> req_header;  //请求报头
    std::string text;  //请求正文

    //存放请求行解析后的内容
    std::string method;
    std::string url;
    std::string http_version;

    std::string req_path;  //请求的路径
    std::string suffix;    //请求资源的后缀
};

//确定Content-Type字段的类
class Suffix
{
public:
    Suffix()
    {
        SuffixInit();
    }
    void SuffixInit()
    {
        content_type.insert({".html", "text/html"});
        content_type.insert({".png", "image/png"});
    }
    std::string SuffixToDesc(std::string suffix)
    {
        auto iter = content_type.find(suffix);
        if(iter == content_type.end()) return content_type[".html"];
        else return content_type[suffix];
    }
public:
    std::unordered_map<std::string, std::string> content_type;
};

//服务器
class HttpServer
{
public:
    HttpServer(const uint16_t serverport = defaultport)
    :_serverport(serverport)
    {}
    void InitServer()
    {
        _listen_sockfd.Socket();
        _listen_sockfd.Bind(_serverport);
        _listen_sockfd.Listen();
    }
    void StartServer()
    {
        while(true)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = _listen_sockfd.Accept(&clientip, &clientport);
            if(sockfd < 0)
                continue;

            pthread_t tid;
            ThreadData* td = new ThreadData(sockfd);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

    //支持线程的函数
    static void HandlerHttp(int sockfd)
    {
        //读取HTTP请求报文
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer)-1, 0); //和read一样都是面向字节流
        if(n > 0)
        {
            buffer[n] = 0;
            //解析请求报文中请求行的url部分
            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();
            req.DebugPrint();

            //构建响应报文
            //响应正文
            bool read_state = true; //是否读取成功的标记位
            std::string text = ReadHtmlContent(req.req_path);   //这里是硬编码路径，不管以何种路径请求都是index.html这个页面
            if(text == "404")   //如果返回404，则是打开文件失败，说明找不到文件
            {
                read_state = false;
                std::string fourxx_err_path = "wwwroot/fourxx_err.html";   //则读取我们写的404页面
                text = ReadHtmlContent(fourxx_err_path);
            }
            //状态行
            std::string response_line;
            if(read_state)
                response_line = "HTTP/1.0 200 OK\r\n";
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";
            
            // response_line = "HTTP/1.0 302 OK\r\n";  //重定向状态码
            //响应报头
            //响应正文长度字段
            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size());
            response_header += "\r\n";
            //响应Location字段
            //response_header += "Location: https://www.qq.com/\r\n";
            //响应Content-Type字段
            Suffix suffix;
            std::string content_type = suffix.SuffixToDesc(req.suffix);
            response_header += "Content-Type: ";
            response_header += content_type;
            response_header += "\r\n";
            response_header += "Set-Cookie: name=ys&&password=123";
            response_header += "\r\n";
            //空行
            std::string blank_line = "\r\n";

            //合并成响应报文
            std::string response = response_line;
            response += response_header;
            response += blank_line;
            response += text;

            //返回响应报文
            send(sockfd, response.c_str(), response.size(), 0);   //和write功能一样
        }
        close(sockfd);
    }

    //支持线程函数
    static void* ThreadRun(void* args)
    {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);

        HandlerHttp(td->_sockfd);
        delete td;
        return nullptr;
    }

    //读取页面代码的函数，获取构建相应正文字段的函数
    static std::string ReadHtmlContent(const std::string& path)
    {
        struct stat st;
        stat(path.c_str(), &st);

        std::ifstream in(path, std::ios::binary); //以二进制进行读取
        //判定是否能打开该文件，，判断该文件是不是目录文件，不能打开或者是目录就返回404
        if(!in.is_open() || S_ISDIR(st.st_mode)) return "404";

        //以二进制形式打开的读取方式
        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);
        
        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());  //不太规范

        //读取文本的时候可以
        // std::string content;
        // std::string line;
        // while(std::getline(in, line))
        // {
        //     content += line;
        // }
        in.close();
        return content;
    }
private:
    Sock _listen_sockfd;
    uint16_t _serverport;
};


//1. 先进行TCP连接
//2. recv读取HTTP请求报文，对报文进行解析----解析出method、url、httpversion等字段
//3. 通过url路径，把用户请求的网页读取出来并构建出相应报文，send给用户




// 读取文件
// 使用 <sys/stat.h>
// struct stat st;
// stat(path.c_str, &st);
// 判定是否文件夹
// S_ISDIR(st.st_mode)
// 判定是否文件
// S_ISREG(st.st_mode)
